package pt.inescid.components.policyengine.heimdall.pap.base;

import java.util.Vector;

import pt.inescid.components.policyengine.heimdall.base.EventFieldCode;
import pt.inescid.components.policyengine.heimdall.base.EventObj;
import pt.inescid.components.policyengine.heimdall.base.EventRelevance;
import pt.inescid.components.policyengine.heimdall.base.User;
import pt.inescid.components.policyengine.heimdall.commons.Utilities;
import pt.inescid.components.policyengine.heimdall.pdp.base.ModelInstance;

public class ModelDefinition {

	private int id;
	private String tag;
	private String monitoredCes;
	
	private Vector ces;
	private Vector domains;
	private Vector policies;
	private Vector reevaluations;
	
	private String modelAsText;
	
	public Vector allRelevantEventFields = null;
	public Vector allRelevantCes = null;
	public Vector allRelevantCesParameters = null;
	public Vector allFieldCodes = null;
	

	
	/* ===========================================================
	==============================================================
	=============                    =============================
	=============      Constructor       =============================
	=============                    =============================
	==============================================================
	=========================================================== */
	public ModelDefinition(String tag) {
		this.tag = tag;
		
		CesDefinition currentCes = null;
		ces = new Vector();

		/* ===========================================================
		=============      root       =============================
		=========================================================== */
		currentCes = new CesDefinition(
				CesDefinition.ROOT_CES, 
				CesDefinition.ROOT_CES, 
				this,
				null,
				this);
		ces.add(currentCes);

		/* ===========================================================
		=============      AllEvents       =============================
		=========================================================== */
		currentCes = new CesDefinition(CesDefinition.ALL_EVENT_CES, 
				CesDefinition.ROOT_CES, 
				this,
				null,
				this);
		currentCes.setRestriction(
				new SimpleDefinition(
						RuleDefinition.LITERAL_EXPRESSION,
						RuleDefinition.EXPRESSION_VALUE_CODES[
						        RuleDefinition.BOOLEAN_VALUE],
						null,
						"true"));
		ces.add(currentCes);

		/* ===========================================================
		=============      PastEvents       =============================
		=========================================================== */
		currentCes = new CesDefinition("PastEvents", 
				CesDefinition.ALL_EVENT_CES, 
				this,
				null,
				this);
		CompositeDefinition currentCondition = null;
		currentCondition = new CompositeDefinition(
				RuleDefinition.GREATER_THAN_OPERATOR,
				null);
		currentCondition.addOperand(
				new EventFieldDefinition(
						this,
						RuleDefinition.EVENT_FIELD_EXPRESSION,
						RuleDefinition.EXPRESSION_VALUE_CODES[
						        RuleDefinition.TIMESTAMP_VALUE],
						currentCondition,
						"ce.executionTime"));
		currentCondition.addOperand(
				new EventFieldDefinition(
						this,
						RuleDefinition.EVENT_FIELD_EXPRESSION,
						RuleDefinition.EXPRESSION_VALUE_CODES[
						        RuleDefinition.TIMESTAMP_VALUE],
						currentCondition,
						".executionTime"));
		currentCes.setRestriction(currentCondition);
		ces.add(currentCes);

		/* ===========================================================
		=============      FutureEvents       =============================
		=========================================================== */
		currentCes = new CesDefinition("FutureEvents", 
				CesDefinition.ALL_EVENT_CES, 
				this,
				null,
				this);
		currentCondition = new CompositeDefinition(
				RuleDefinition.LESSER_THAN_OPERATOR,
				null);
		currentCondition.addOperand(
				new EventFieldDefinition(
						this,
						RuleDefinition.EVENT_FIELD_EXPRESSION,
						RuleDefinition.EXPRESSION_VALUE_CODES[
						        RuleDefinition.TIMESTAMP_VALUE],
						currentCondition,
						"ce.executionTime"));
		currentCondition.addOperand(
				new EventFieldDefinition(
						this,
						RuleDefinition.EVENT_FIELD_EXPRESSION,
						RuleDefinition.EXPRESSION_VALUE_CODES[
						        RuleDefinition.TIMESTAMP_VALUE],
						currentCondition,
						".executionTime"));
		currentCes.setRestriction(currentCondition);
		ces.add(currentCes);
		
		domains = new Vector();
		domains.add(new DomainDefinition("localhost", "", true));
		
		policies = new Vector();
		reevaluations = new Vector();
		
	}
	
	
	
	
	public void addPolicy(PolicyDefinition newPolicy) {
		policies.add(newPolicy);
	}
	
	public void addDomain(DomainDefinition newDomain) {
		domains.add(newDomain);
	}
	
	public void addCes(CesDefinition newCes) {
		ces.add(newCes);
	}
	
//  ----------------------------------------------------------
//  ------------------------   toString   ------------------------
//  ----------------------------------------------------------
	public String toString() {
		String result = "";
		
		for (int i = 0; i < domains.size(); i++) {
			result += (DomainDefinition) domains.elementAt(i);
		}
		
		for (int i = 0; i < ces.size(); i++) {
			result += (CesDefinition) ces.elementAt(i);
		}
		
		for (int i = 0; i < policies.size(); i++) {
			result += (PolicyDefinition) policies.elementAt(i);
		}
		
		return (result);
	}

//  ----------------------------------------------------------
//  ------------------------   getFunction   ------------------------
//  ----------------------------------------------------------
	public String getFunction() {
		String result = "";
		
		result += "\n--- DOMAINS ---\n";
		for (int i = 0; i < domains.size(); i++) {
			result += ((DomainDefinition) domains.elementAt(i)).getFunction() +
						"\n";
		}
		
		result += "\n--- CES ---\n";
		for (int i = 0; i < ces.size(); i++) {
			result += ((CesDefinition) ces.elementAt(i)).getFunction() +
					"\n";
		}
		
		result += "\n--- POLICIES ---\n";
		for (int i = 0; i < policies.size(); i++) {
			result += ((PolicyDefinition) policies.elementAt(i)).getFunction() +
						"\n";
		}
		
		return (result);
	}


//  ----------------------------------------------------------
//  ------------------------   getEvaluationInstance   ------------------------
//  ----------------------------------------------------------
	public ModelInstance getEvaluationInstance(User currentUser, EventObj ce) {
		return (new ModelInstance(currentUser, this, ce));
	}

	public Vector getCes() {
		return ces;
	}

	public Vector getDomains() {
		return domains;
	}

	public Vector getPolicies() {
		return policies;
	}

	public void setCes(Vector ces) {
		this.ces = ces;
	}

	public void setDomains(Vector domains) {
		this.domains = domains;
	}

	public void setPolicies(Vector policies) {
		this.policies = policies;
	}


	public CesDefinition getCes(String cesName) {
		if (cesName == null) {
			return (null);
		}
		
		for (int i = 0; i < ces.size(); i++) {
			if (((CesDefinition) ces.elementAt(i)).getName().
					equals(cesName)) {
				return ((CesDefinition) ces.elementAt(i));
			}
		}
		
//		System.out.println("Ces not found: " + cesName);
		return (null);
	}

//	public CesDefinition getCes(int cesId) {
//		if (cesName.equals(CesDefinition.ROOT_CES)) {
//			return (null);
//		}
//		
//		for (int i = 0; i < ces.size(); i++) {
//			if (((CesDefinition) ces.elementAt(i)).getId() == cesId) {
//				return ((CesDefinition) ces.elementAt(i));
//			}
//		}
//		
//		System.out.println("Ces not found: " + cesId);
//		return (null);
//	}
//
	public DomainDefinition getDomain(String domainName) {
		if (domainName == null) {
			domainName = "localhost";
		}
		
		for (int i = 0; i < domains.size(); i++) {
			if (((DomainDefinition) domains.elementAt(i)).getName().
					equals(domainName)) {
				return ((DomainDefinition) domains.elementAt(i));
			}
		}
		
		System.out.println("Domain not found: " + domainName);
		return (null);
	}


	public PolicyDefinition getPolicy(String policyName) {
		if (policyName == null) {
			return (null);
		}
		
		for (int i = 0; i < policies.size(); i++) {
			if (((PolicyDefinition) policies.elementAt(i)).getName().
					equals(policyName)) {
				return ((PolicyDefinition) policies.elementAt(i));
			}
		}
		
		// if the policy is not found, we assume it is a 
		// subpolicy definition for a remote policy and create a 
		// new definition
		PolicyDefinition currentPolicy = new PolicyDefinition(policyName, true);
		addPolicy(currentPolicy);

		return (currentPolicy);
	}

//  ----------------------------------------------------------
//  ------------------------   isInfoField   ------------------------
//  ----------------------------------------------------------
	public boolean isInfoField(int fieldId) {
		if (allRelevantEventFields == null) {
			init();
		}

		EventRelevance currentRelevance = null;
		for (int i = 0; i < allRelevantEventFields.size(); i++) {
			currentRelevance = (EventRelevance) 
					allRelevantEventFields.elementAt(i);
			
			if (getFieldId(currentRelevance.getFieldName()) == fieldId) {
				return (true);
			}
		}
		return (false);
	} // isInfoField

	
	
//  ----------------------------------------------------------
//  ------------------------   getRelevantEventFields   ------------------------
//  ----------------------------------------------------------

	
	
	
	
//  ----------------------------------------------------------
//  ------------------------   getRelevantCes   ------------------------
//  ----------------------------------------------------------
	public Vector getRelevantCes() {
		Vector allRelevant = new Vector();
		Vector currentRelevant = null;
		PolicyDefinition currentPolicy = null;
		
		for (int i = 0; i < policies.size(); i++) {
			currentPolicy = (PolicyDefinition) policies.elementAt(i);
			currentRelevant = currentPolicy.getRelevantCes();
			Utilities.joinCesNoDuplicates(allRelevant, currentRelevant);
		}
		
		return (allRelevant);
	}

//  ----------------------------------------------------------
//  ------------------------   getRelevantEventParameters   ------------------------
//  ----------------------------------------------------------
//	public Vector getRelevantEventParameters(EventObj currentEvent) {
//		Vector result = new Vector();
//		
//		CesDefinition currentCes = null;
//		EventRelevance currentRelevance = null;
//		for (int i = 0; i < ces.size(); i++) {
//			currentCes = (CesDefinition) ces.elementAt(i);
//			if (currentCes.isRelevant(currentEvent)) {
//				result.add(new EventRelevanceeventTag += "1";
//			}
//			else {
//				eventTag += "0";
//			}
//		}
//		
//		return (result);
//	}

	/* ===========================================================
	==============================================================
	=============                    =============================
	=============      getAllCesParameters       =============================
	=============                    =============================
	==============================================================
	=========================================================== */
	public Vector getAllCesParameters1() {
		Vector allParameters = new Vector();
		Vector currentParameters = null;
		CesDefinition currentCes = null;
		
		for (int i = 0; i < ces.size(); i++) {
			currentCes = (CesDefinition) ces.elementAt(i);
			currentParameters = currentCes.getCesParameters();
			currentParameters = Utilities.removeDuplicates(currentParameters);
			Utilities.joinParametersNoDuplicates(allParameters, 
					currentParameters);
		}
		
		return (allParameters);
	} // getAllCesParameters


	
	
	/* ===========================================================
	==============================================================
	=============                    =============================
	=============      getRelevantCesParameters       =============================
	=============                    =============================
	==============================================================
	=========================================================== */
	public Vector getRelevantCesParameters() {
		if (allRelevantCesParameters == null) {
			init();
		}

		return (allRelevantCesParameters);
	} // getRelevantCesParameters



	/* ===========================================================
	==============================================================
	=============                    =============================
	=============      init       =============================
	=============                    =============================
	==============================================================
	=========================================================== */
	public void init() {
		
		// check all relevant event fields, ces and ces parameters
		allRelevantEventFields = new Vector();
		allRelevantCes = new Vector();
		
		PolicyDefinition currentPolicy = null;
		Vector currentRelevantEventFields = null;
		Vector currentRelevantCes = null;
		for (int i = 0; i < policies.size(); i++) {
			currentPolicy = (PolicyDefinition) policies.elementAt(i);			
			
			currentRelevantEventFields = 
				currentPolicy.getRelevantEventFields();
			Utilities.joinParametersNoDuplicates(allRelevantEventFields,
					currentRelevantEventFields);
			
			currentRelevantCes = currentPolicy.getRelevantCes();
			Utilities.joinCesNoDuplicates(allRelevantCes,
					currentRelevantCes);
			
		}
		

		allRelevantCesParameters = new Vector();
		Vector currentParameters = null;
		CesDefinition currentCes = null;
		
		for (int i = 0; i < allRelevantCes.size(); i++) {
			currentCes = (CesDefinition) allRelevantCes.elementAt(i);
			
			currentParameters = currentCes.getCesParameters();
			currentParameters = Utilities.removeDuplicates(currentParameters);
			Utilities.joinParametersNoDuplicates(
					allRelevantCesParameters, 
					currentParameters);
			
			currentRelevantEventFields = 
				currentCes.getRelevantEventFields();
			Utilities.joinParametersNoDuplicates(allRelevantEventFields,
					currentRelevantEventFields);
		}
		
		
		allFieldCodes = new Vector();
		EventRelevance currentRelevance = null;
		EventFieldCode currentCode = null;
		boolean found = false;
		for (int i = 0; i < allRelevantEventFields.size(); i++) {
			currentRelevance = (EventRelevance) allRelevantEventFields.
					elementAt(i);
			
			found = false;
			for (int j = 0; j < allFieldCodes.size(); j++) {
				currentCode = (EventFieldCode) allFieldCodes.elementAt(j);
				
				if (currentCode.getName().equals(
						currentRelevance.getFieldName())) {
					found = true;
					j = allFieldCodes.size();
				}
			}
			if (!found) {
				allFieldCodes.add(new EventFieldCode(
						currentRelevance.getFieldName(),
						currentRelevance.getFieldType()));
			}
		}
		
		for (int i = 0; i < allRelevantCesParameters.size(); i++) {
			currentRelevance = (EventRelevance) allRelevantCesParameters.
					elementAt(i);
			
			found = false;
			for (int j = 0; j < allFieldCodes.size(); j++) {
				currentCode = (EventFieldCode) allFieldCodes.elementAt(j);
				
				if (currentCode.getName().equals(
						currentRelevance.getFieldName())) {
					found = true;
					j = allFieldCodes.size();
				}
			}
			if (!found) {
				allFieldCodes.add(new EventFieldCode(
						currentRelevance.getFieldName(),
						currentRelevance.getFieldType()));
			}
		}

		allFieldCodes = allFieldCodes;
	} // init

	/* ===========================================================
	==============================================================
	=============                    =============================
	=============      getAllRelevantEventFields       =============================
	=============                    =============================
	==============================================================
	=========================================================== */
	public Vector getAllRelevantEventFields() {
		if (allRelevantEventFields == null) {
			init();
		}
		
		return allRelevantEventFields;
	}

	/* ===========================================================
	==============================================================
	=============                    =============================
	=============      getAllRelevantCes       =============================
	=============                    =============================
	==============================================================
	=========================================================== */
	public Vector getAllRelevantCes() {
		if (allRelevantCes == null) {
			init();
		}
		
		return allRelevantCes;
	}
	
	/* ===========================================================
	==============================================================
	=============                    =============================
	=============      getRelevantCesIndex       =============================
	=============                    =============================
	==============================================================
	=========================================================== */
	public int getRelevantCesIndex(String cesName) {
		if (allRelevantCes == null) {
			init();
		}
		
		CesDefinition currentCes = null;
		for (int i = 0; i < allRelevantCes.size(); i++) {
			currentCes = (CesDefinition) allRelevantCes.elementAt(i);
			if (currentCes.getName().equals(cesName)) {
				return (i);
			}
		}
		
		return (Utilities.UNDEFINED_INT_VALUE);
	}

	
	/* ===========================================================
	==============================================================
	=============                    =============================
	=============      isObligation       =============================
	=============                    =============================
	==============================================================
	=========================================================== */
	public boolean isObligation() {
		return (((PolicyDefinition) policies.elementAt(0)).
				isObligation());
	} // isObligation
	
	
	
	/* ===========================================================
	==============================================================
	=============                    =============================
	=============      getObligationTimeout       =============================
	=============                    =============================
	==============================================================
	=========================================================== */
	public long getObligationTimeout() {
		return (((PolicyDefinition) policies.elementAt(0)).
				getObligationTimeout());
	} // getObligationTimeout
	

	
	/* ===========================================================
	==============================================================
	=============                    =============================
	=============      getFieldName       =============================
	=============                    =============================
	==============================================================
	=========================================================== */
	public String getFieldName(int fieldId) {
		if (allFieldCodes == null) {
			init();
		}
		
		EventFieldCode currentCode = null;
		for (int j = 0; j < allFieldCodes.size(); j++) {
			currentCode = (EventFieldCode) allFieldCodes.elementAt(j);
			
			if (currentCode.getId() == fieldId) {
				return (currentCode.getName());
			}
		}
		return (null);
	} // getFieldName
	
	
	/* ===========================================================
	==============================================================
	=============                    =============================
	=============      getFieldId       =============================
	=============                    =============================
	==============================================================
	=========================================================== */
	public int getFieldId(String fieldName) {
		if (allFieldCodes == null) {
			init();
		}
		
		if (!fieldName.startsWith(".")) {
			fieldName = fieldName.substring(fieldName.indexOf("."));
			System.out.println("fName: " + fieldName);
		}
		
		EventFieldCode currentCode = null;
		for (int j = 0; j < allFieldCodes.size(); j++) {
			currentCode = (EventFieldCode) allFieldCodes.elementAt(j);
			
			if (currentCode.getName().equals(fieldName)) {
				return (currentCode.getId());
			}
		}
		return (Utilities.UNDEFINED_INT_VALUE);
	} // getFieldId
	
	

	
	

	
	
	public int getId() {
		return id;
	}

	public String getModelAsText() {
		return modelAsText;
	}

	public void setModelAsText(String modelAsText) {
		this.modelAsText = modelAsText;
	}

	public String getTag() {
		return tag;
	}

	public void setId(int id) {
		this.id = id;
	}

	public void setTag(String tag) {
		this.tag = tag;
	}

	public String getMonitoredCes() {
		return monitoredCes;
	}

	public void setMonitoredCes(String monitoredCes) {
		this.monitoredCes = monitoredCes;
	}

	public Vector getReevaluations() {
		return reevaluations;
	}

	public void addReevaluation(ReevaluationDefinition reeval) {
		this.reevaluations.add(reeval);
	}

}; // ModelDefinition
